package org.chen.service;

import com.alibaba.nacos.common.utils.StringUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.chen.model.vo.CrawlerRuleVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class CrawlerRuleService {

    private static final String CACHE_KEY = "crawler:rules";
    private static final String LOCK_KEY = "crawler:rules:lock";
    private static final long CACHE_TIMEOUT = 720; // 缓存过期时间(分钟)
    private static final long LOCK_TIMEOUT = 10; // 分布式锁超时时间(秒)

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 获取爬虫规则列表
     */
    public List<CrawlerRuleVO> getRules() {
        return List.of(
                CrawlerRuleVO.builder()
                        .id("soccer")
                        .name("ESPN足球新闻")
                        .description("爬取ESPN足球新闻内容")
                        .build(),
                CrawlerRuleVO.builder()
                        .id("basketball")
                        .name("ESPN篮球新闻")
                        .description("爬取ESPN篮球新闻内容")
                        .build(),
                CrawlerRuleVO.builder()
                        .id("rugby")
                        .name("ESPN棒球新闻")
                        .description("爬取ESPN棒球新闻内容")
                        .build(),
                CrawlerRuleVO.builder()
                        .id("sports-betting")
                        .name("ESPN体育博彩新闻")
                        .description("爬取ESPN体育博彩新闻内容")
                        .build(),
                CrawlerRuleVO.builder()
                        .id("nba")
                        .name("ESPN-NBA新闻")
                        .description("爬取ESPN-NBA新闻内容")
                        .build(),
                CrawlerRuleVO.builder()
                        .id("transfers")
                        .name("ESPN-转会新闻")
                        .description("爬取ESPN-转会新闻内容")
                        .build()
        );
    }

    /**
     * 项目启动时将爬虫规则缓存到 Redis
     */
    @PostConstruct
    public void cacheRules() {
        List<CrawlerRuleVO> rules = getRules();
        // 序列化数据
        String cacheValue;
        try {
            cacheValue = objectMapper.writeValueAsString(rules);
            redisTemplate.opsForValue().set(
                    CACHE_KEY,
                    cacheValue,
                    CACHE_TIMEOUT,
                    TimeUnit.MINUTES
            );
            System.out.println("爬虫规则已缓存到 Redis");
        } catch (JsonProcessingException e) {
            log.error("爬虫规则缓存异常", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取爬虫规则列表(带缓存)
     */
    public List<CrawlerRuleVO> getRulesWithCache() {
        try {
            // 从Redis获取缓存
            String cacheValue = redisTemplate.opsForValue().get(CACHE_KEY);

            if (StringUtils.isEmpty(cacheValue)) {
                // 缓存不存在,触发异步刷新
                refreshCacheAsync();
                throw new RuntimeException("缓存不存在，正在重新加载数据，请稍后重试");
            }

            // 反序列化缓存数据
            return objectMapper.readValue(cacheValue,
                    new TypeReference<List<CrawlerRuleVO>>() {});

        } catch (Exception e) {
            log.error("获取爬虫规则缓存异常", e);
            throw new RuntimeException("获取数据异常");
        }
    }

    /**
     * 异步刷新缓存
     */
    @Async
    public void refreshCacheAsync() {
        try {
            // 尝试获取分布式锁
            Boolean locked = redisTemplate.opsForValue()
                    .setIfAbsent(LOCK_KEY, "1", LOCK_TIMEOUT, TimeUnit.SECONDS);

            if (Boolean.TRUE.equals(locked)) {
                try {
                    // 获取最新数据
                    List<CrawlerRuleVO> rules = getRules();

                    // 序列化数据
                    String cacheValue = objectMapper.writeValueAsString(rules);

                    // 更新缓存(设置过期时间)
                    redisTemplate.opsForValue().set(
                            CACHE_KEY,
                            cacheValue,
                            CACHE_TIMEOUT,
                            TimeUnit.MINUTES
                    );

                    log.info("爬虫规则缓存刷新成功");
                } finally {
                    // 释放锁
                    redisTemplate.delete(LOCK_KEY);
                }
            }
        } catch (Exception e) {
            log.error("刷新爬虫规则缓存异常", e);
        }
    }
}
